/**
 * rayTracerPart2.cpp
 *
 * Author:
 *   Chris Herbison
 *
 * Description:
 *   This program simulates the basic algorithm of ray tracing, in so far as
 *   determining what color to make each pixel without dealing with lighting.
 */

#include <stdlib.h>
#include <time.h>

#ifdef GDD_LAB_COMPUTER
#include <glut.h>
#else
#include <gl/glut.h>
#endif

#include "MyColor.h"
#include "vecmath.h"
#include "Ray.h"
#include "World.h"
#include "Object.h"
#include "Camera.h"
#include "Sphere.h"
#include "Triangle.h"
#include "Rectangle.h"
#include "Light.h"
#include "PhongI.h"
#include "IlluminationModel.h"
#include "IlluminationData.h"
#include "TextureData.h"
#include "IntersectData.h"
#include "WorldData.h"
#include "PlyFileParser.h"
#include "KDTree.h"
#include "KDTreeNode.h"
#include "BoundingBox.h"

//Constants

//BG_COLOR can be found in Camera.h

//Constants for the camera.
const Point3 camPos(278, 273, -800);
//const Point3 camPos(0, 0.15, 0.2);
//const Point3 camPos(0, 0, 2);
const Vector3 camLook(0, 0, 1);
const Vector3 camUp(0, 1, 0);

//Constants for the light.
//const Point3 l1Pos(278, 100, 279.5);
const Point3 l1Pos(278, 200, -100);
//const Point3 l1Pos(0, 5, 10);
const MyColor l1Color(0.5,0.5,0.5,1);
const MyColor lightInitEnergy(10000,10000,10000,1);

//Constants for model's lighting and color
const float modelAmbK  = 0.2;
const float modelDiffK = 1.0;
const float modelSpecK = 1.0;
const float modelExpK  = 1.0;
const float modelReflective   = 0;
const float modelTransmissive = 0;
const MyColor modelAmbColor(0.0, 0.0, 0.0, 1);
const MyColor modelDifColor(0.96, 0.96, 0.86, 1);
const MyColor modelSpecular(0.0, 0.0, 0.0, 1);

//Constants for TextureData.
const int XY = 0;
const int XZ = 1;
const int YZ = 2;
const bool STRETCH = true;
const bool REPEAT = false;

//Constants for Tone Reproduction.
const bool WARD = true;
const bool REINHARD = false;
const double Lmax = 10000;

//Constants for radiosity calculation.
const size_t radiosityIterations = 100;


//Variable used to prevent the scene from re-rendering once it has started.
static bool sceneRendered = false;

/*
 * Sets up the objects, world, and camera then renders the scene.
 */
void setUpAndRender(){
	cout << "Screen refreshed." << endl;
	if(!sceneRendered){
		sceneRendered = true;
		glClear( GL_COLOR_BUFFER_BIT );

		Camera sceneCam(camPos, camLook, camUp, Lmax, WARD);
		World sceneWorld;
     
		//Add the light to the scene
		Light l1(l1Pos, l1Color);
		sceneWorld.add(l1);

		//Define illumination data for all triangles read in
		IlluminationData modelIllumination(modelAmbK, modelSpecK, modelDiffK, modelExpK, modelReflective, modelTransmissive);		
		TextureData modelTexture(modelAmbColor, modelDifColor, modelSpecular);

		//Read in the triangles from the ply file
		vector<Rectangle> sceneObjects;
		//string polygonFile = "E:\\My Documents\\Code\\C++\\Advanced Rendering\\models\\box\\cornellBoxSimpler.ply";
		string polygonFile = "E:\\My Documents\\Code\\C++\\Advanced Rendering\\models\\box\\cornellBox.ply";
		PlyFileParser p;
		//vector<vector<Point3> > loadedTriangles;
		vector<vector<Point3> > loadedRectangles;
		cout << "Parsing polygon file." << endl;
		//loadedTriangles = p.parseFile(polygonFile);
		loadedRectangles = p.parseFileRectColor(polygonFile);

		/*Point3 r1v1(-1,-1,1);
		Point3 r1v2(-1,1,1);
		Point3 r1v3(1,1,1);
		Point3 r1v4(1,-1,1);
		Vector3 r1normal = (r1v3-r1v2)^(r1v3-r1v1);
		Point3 r1n(r1normal.x,r1normal.y,r1normal.z);
		vector<Point3> r1;
		r1.push_back(r1v1);
		r1.push_back(r1v2);
		r1.push_back(r1v3);
		r1.push_back(r1v4);
		r1.push_back(r1n);
		r1.push_back(Point3(1,1,1));
		loadedRectangles.push_back(r1);*/

		time_t beforeConstruct, afterConstruct;
		time(&beforeConstruct);
		cout << "Load successful, constructing scene with " << loadedRectangles.size() << " objects." << endl;
		//Put the data read in in to world object list
		Vector3 tempNormal;
		vector<Point3> tempVerts(4);
		MyColor tempColor(1, 1, 1, 1);
		MyColor surfaceEnergy;
		TextureData tempTexture(modelAmbColor, modelDifColor, modelSpecular);
		/*for(unsigned int i = 0; i < loadedTriangles.size(); i++){
			tempVerts.at(0) = loadedTriangles.at(i).at(0);
			tempVerts.at(1) = loadedTriangles.at(i).at(1);
			tempVerts.at(2) = loadedTriangles.at(i).at(2);
			tempNormal = Vector3(loadedTriangles.at(i).at(3).x, loadedTriangles.at(i).at(3).y, loadedTriangles.at(i).at(3).z);
			modelTriangles.push_back(Triangle(modelIllumination, modelTexture, tempNormal, tempVerts));
		}*/
		for(unsigned int i = 0; i < loadedRectangles.size(); i++){
			tempVerts.at(0) = loadedRectangles.at(i).at(0);
			tempVerts.at(1) = loadedRectangles.at(i).at(1);
			tempVerts.at(2) = loadedRectangles.at(i).at(2);
			tempVerts.at(3) = loadedRectangles.at(i).at(3);
			tempNormal = Vector3(loadedRectangles.at(i).at(4).x, loadedRectangles.at(i).at(4).y, loadedRectangles.at(i).at(4).z);
			
			tempColor.r = loadedRectangles.at(i).at(5).x;
			tempColor.g = loadedRectangles.at(i).at(5).y;
			tempColor.b = loadedRectangles.at(i).at(5).z;
			if(tempColor.r == 1 && tempColor.g == 1 && tempColor.b == 1){
				loadedRectangles.at(i);
				surfaceEnergy = lightInitEnergy;
			}
			else{
				surfaceEnergy = MyColor(0,0,0,1);
			}
			tempTexture.ca = tempColor;
			tempTexture.cd = tempColor;
			sceneObjects.push_back(Rectangle(modelIllumination, tempTexture, tempNormal, tempVerts, surfaceEnergy));
		}

		//Add the polygons to the scene
		vector<Rectangle*> objectPointers;
		for(unsigned int i = 0; i < sceneObjects.size(); i++){
			sceneWorld.add(&sceneObjects.at(i));
			objectPointers.push_back(&sceneObjects.at(i));
		}
		time(&afterConstruct);
		float constructTime = difftime(afterConstruct, beforeConstruct);
		cout << "Scene constructed in " << constructTime << " seconds." << endl;

		cout << "Scene constructed, running radiosity calculations with " << radiosityIterations << " iterations." << endl;
		time_t beforeRadiosity, afterRadiosity;
		time(&beforeRadiosity);
		//Do radiant exitance calculations
		sceneCam.distributeRadiantEnergy(objectPointers, radiosityIterations);
		time(&afterRadiosity);
		float radTime = difftime(afterRadiosity, beforeRadiosity);
		cout << "Radiosity calculations took " << radTime << " seconds to calculate." << endl;

		/*cout << "Building k-d tree." << endl;
		time_t beforeTree, afterTree;
		time(&beforeTree);
		//Generate the KD-Tree
		BoundingBox sceneBox;
		sceneBox.minValues = Point3(-1,-1,-1);
		sceneBox.maxValues = Point3( 1, 1, 1);
		KDTree theTree;
		theTree.buildTree(&sceneObjects, sceneBox);
		sceneWorld.setTree(theTree);
		time(&afterTree);
		float kdTime = difftime(afterTree, beforeTree);
		cout << "K-D tree took " << kdTime << " seconds to generate." << endl;*/

		//Determine the focal distance of the virtual viewpoint
		//float focalLength = -150;
		float focalLength = 400;
		Point3 rayOrigin(sceneCam.position);

		//Create a ray for each pixel
		for(int x = -150; x < 150; x++){
			for(int y = -150; y < 150; y++){
			   Vector3 rayDir(x,y,focalLength);
			   Ray rTemp(rayOrigin, rayDir);
               sceneWorld.spawnRay(rTemp);
			}
		}
		
		time_t beforeRender, afterRender;
		cout << "Rendering scene." << endl;
		time(&beforeRender);
		sceneCam.render(sceneWorld);
		time(&afterRender);
		float renderTime = difftime(afterRender, beforeRender);
		cout << "Scene took " << renderTime << " seconds to render." << endl;
	}

    glFlush();

}

/*
 * Main function
 *
 * returns program's exit status.
 */
int main(){
     glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
     glutCreateWindow("Ray Tracer Written By Chris Herbison");
     glutDisplayFunc(setUpAndRender);
     glClearColor(BG_COLOR.r, BG_COLOR.g, BG_COLOR.b, BG_COLOR.a);
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     gluOrtho2D(0,300,0,300);
     glutMainLoop();
}